#ifndef  PLAYER_HPP
# define PLAYER_HPP

# include <Shinygami/Applejack/body.hpp>
# include <Shinygami/eventmanager.hpp>
# include "attack.hpp"

class Player : public Shiny::Applejack::IA
{
public:
  Player(Shiny::Applejack::World& w, Shiny::Applejack::Body& b, Shiny::Applejack::View* camera);

  virtual void Run(void);

  void SetControls(Shiny::Controls& controls);
  void AddSpellDisplay(Shiny::Widgets::ProgressBar*& spell, int it, const sf::Texture* texture);

  void Moving(sf::Event event);
  void StopMoving(sf::Event event);
  void FootCollision(b2Contact* contact);
  void FootEndCollision(b2Contact* contact);
  void Punch(sf::Event);
  void EndPunch(unsigned int);
  void Kick(sf::Event);
  void EndKick(unsigned int);
  bool IsPunching(void) const { return (_isPunching); }

  virtual void SpellFirst(sf::Event)    {}
  virtual void SpellSecond(sf::Event)   {}
  virtual void SpellThird(sf::Event)    {}
  virtual void SpellUltimate(sf::Event) {}

  virtual void               InterruptChannelling(void) {}
  virtual void               Victory(bool) {}
  virtual void               GetKicked(int, const Attack::Effects&);
  virtual void               Stun(float time);
  Observatory::Signal<float> LifeUpdated;

  template<typename TARGET>
  void                       AddTarget(TARGET* target, Shiny::Applejack::Fixture* fixture, typename Attack::Target::Signal::Observer<TARGET>::Method callback)
  {
      if (!fixture)
	return ;
      std::list<Attack*>::iterator it  = _attacks.begin();
      std::list<Attack*>::iterator end = _attacks.end();

      for (; it != end ; ++it)
      {
	  (*it)->targets.push_front(Attack::Target(fixture));
	  (*it)->targets.begin()->Hit.Connect(*target, callback);
      }
  }

  bool                    _isPunching;

protected:
  void UpdateSpellProgressBar(float elapsedTime, float totalTime, Shiny::Widgets::ProgressBar* widget)
  {
      float percentage = elapsedTime / totalTime * 100;

      widget->SetPercentage(100.f - percentage);
  }

  void         SetDefaultAnimation(void);
  virtual void AnimationInterupted(void);

  void ApplyEffects()
  {
      Attack::Effects::iterator it  = _buffs.begin();
      Attack::Effects::iterator end = _buffs.end();

      while (it != end)
      {
	  Attack::Effect effect = *it;

	  if (effect.duration >= effect.timer.GetElapsedTime().AsMilliseconds())
	  {
	      EndBuff(effect);
	      it = _buffs.erase(it);
	  }
	  else
	      ++it;
      }
  }

  void EndBuff(const Attack::Effect& effect)
  {
      switch (effect.type)
      {
      case Attack::Effect::STUN:
	  break ;
      case Attack::Effect::SPEED_MALUS:
	  _speedModifier += effect.value;
	  break ;
      case Attack::Effect::RESISTANCE_MALUS:
	  _damageResistanceModifier += effect.value;
	  break ;
      }
  }

  void AddBuff(Attack::Effect effect)
  {
      effect.timer.Restart();
      _buffs.push_back(effect);
      switch (effect.type)
      {
      case Attack::Effect::STUN:
	  Stun(effect.duration);
	  break ;
      case Attack::Effect::SPEED_MALUS:
	  _speedModifier -= effect.value;
	  break ;
      case Attack::Effect::RESISTANCE_MALUS:
	  _damageResistanceModifier -= effect.value;
	  break ;
      }
  }

  Shiny::Controls*        _controls;

  bool                    _isKicking;
  bool                    _punchingLeft, _punchingRight, _kickingLeft, _kickingRight;
  std::list<Attack*>      _attacks;
  Attack::Effects         _buffs;
  Attack                  *_attackPunchLeft,   *_attackPunchRight;
  Attack                  *_attackAirKickLeft, *_attackAirKickRight;

  float                   _life;
  float                   _damageResistance, _damageResistanceModifier;
  float                   _speed,           _speedModifier;
  float                   _strength,        _strengthModifier;
  bool                    _movingLeft, _movingUp, _movingRight;
  bool                    _lookingLeft;
  unsigned short          _canJump;
  bool                    _stunned;
  bool                    _performingAction;
  float                   _stunTime;
  sf::Clock               _jumpTimeout, _punchTimeout, _kickTimeout, _stunTimeout;
  Observatory::ObserverId _animationPunchObserver;
  Observatory::ObserverId _animationKickObserver;

  Shiny::Control          *_controlLeft, *_controlUp, *_controlRight;
  Shiny::Control          *_controlPunch;

  bool                    _hudLeft;

  Shiny::Applejack::View* _camera;
  Shiny::Widgets::ProgressBar *spell1, *spell2, *spell3, *spell4;
};

#endif
